home *** CD-ROM | disk | FTP | other *** search
- /*
- * File Name: sample ConverterShell.c
- *
- * Description: This file contains a shell for building low level converters.
- *
- * Written by: David Gelphman 2/09/98
- *
- * Copyright: © 1998 by Apple Computer Inc., all rights reserved.
- *
- *
- * Change History (most recent first):
- *
- * 04/01/98 DMG457 First version.
- */
-
- #include <Types.h>
- #include <TextUtils.h>
- #include <Resources.h>
- #include <CodeFragments.h>
-
- #include "Debug.h"
- #include "sample ConverterShell.h"
- #include "DownloadMgrLib.h"
- #include "FeatureUtilsLib.h"
- #include "PSWriterErr.h"
- #include "Utilities.h"
- #include "Version.h"
-
- /************ Defines ************/
-
- #define DSC30Version 0x30000 // Fixed(3);
- #define EPSF30Version 0x30000 // Fixed(3);
-
- #define doOutputPosition(subsectionString, info) OutputPosition(comm, hints, (subsectionString), (info), isNotEPS)
-
-
- /************ Prototypes ************/
-
- static OSStatus GetCollectionStrOrDef(Collection hints, CollectionTag tag, long id,
- StringPtr str, long strSize, StringPtr defaultStr);
-
- static OSErr emitHintStr(StreamInfoData comm, const SubsectionStr *form, Collection hints, CollectionTag tag, long id,
- ConstStringPtr defaultStr);
- static OSStatus headerDate(StreamInfoData comm);
-
- static OSStatus OutputPosition(StreamInfoData comm, Collection hints, const SubsectionStr *subsectionStr, void *info, Boolean isNotEPS);
- OSErr InitLowConverterLib(CFragInitBlockPtr initBlkPtr);
- static UInt32 computeLanguageLevel(SInt32 hintLanguageLevel);
-
-
- /********* Static Global Data *********/
-
- static const struct SubsectionStr
- psLowVersion = {"\p%!PS-Adobe-3.0\r", kSubPSAdobe},
- psLowEPSFVersion = {"\p%!PS-Adobe-3.0 EPSF-3.0\r", kSubPSAdobeEPS},
- psLowBoundingBox = {"\p%%BoundingBox: 0 0 ^d ^d\r", kSubBoundingBox},
- psLowBeginProlog = {"\p%%BeginProlog\r", kSubBeginProlog},
- psLowEndComments = {"\p%%EndComments\r", kSubEndComments},
- psLowEndProlog = {"\p%%EndProlog\r", kSubEndProlog},
- psLowBeginSetup = {"\p%%BeginSetup\r", kSubBeginSetup},
- psLowEndSetup = {"\p%%EndSetup\r", kSubEndSetup},
- psLowPage = {"\p%%Page: ^d ^d\r", kSubPage},
- psLowEOF = {"\p%%EOF\r", kSubEOF},
- psLowBeginPageSetup = {"\p%%BeginPageSetup\r", kSubBeginPageSetup},
- psLowEndPageSetup = {"\p%%EndPageSetup\r", kSubEndPageSetup},
- psLowPageTrailer = {"\p%%PageTrailer\r", kSubPageTrailer},
- psLowDocTrailer = {"\p%%Trailer\r", kSubTrailer},
- psLowPagesKnown = {"\p%%Pages: ^d\r", kSubPages},
- psLowLanguageLevel = {"\p%%LanguageLevel: ^d\r", kSubLangLevel},
- psLowDocDataClean7Bit = {"\p%%DocumentData: Clean7Bit\r", kSubDocData},
- psLowDocDataBinary = {"\p%%DocumentData: Binary\r", kSubDocData},
- psLowDocTitle = {"\p%%Title: (^T)\r", kSubTitle},
- psLowDocFor = {"\p%%For: (^T)\r", kSubFor},
- psLowDocRouting = {"\p%%Routing: (^T)\r", kSubRouting},
- psLowDocCreationDate = {"\p%%CreationDate: (^T)\r", kSubDate},
- psLowDocCreator = {"\p%%Creator: (^T)\r", kSubCreator},
- psLowShowPage = {"\p\rshowpage\r", kSubShowpage};
-
- static const unsigned char psBeforeDeviceMatrixAdjust[] = "\p[\r";
- static const unsigned char psAfterDeviceMatrixAdjust[] = "\pcounttomark 1 eq{concat}{cleartomark}ifelse\r";
-
- static FSSpec gLowConverterFileSpec;
-
- // end of global data
-
- OSErr InitLowConverterLib(CFragInitBlockPtr initBlkPtr)
- /* Called before the Low Level Converter Library is loaded. We use this chance
- to save away our FSSpec.
- */
- {
- OSErr err = noErr;
-
- /* If this library was loaded from a file, then save away the file's
- FSSpec. This way we can get to the library's resource fork to
- get our resources.
- */
- if(initBlkPtr->fragLocator.where == kDataForkCFragLocator){
- gLowConverterFileSpec = *initBlkPtr->fragLocator.u.onDisk.fileSpec;
- }else{
- err = -1;
- }
-
- return err;
- }
-
- #if PRAGMA_IMPORT_SUPPORTED
- #pragma export on
- #endif
-
-
- OSStatus psLowGetConverterInformation(const ConverterDescription **theDescriptionPtr)
- {
- OSStatus err = noErr;
- ConverterDescription *thePtr;
-
- err = converterGetConverterInfoPtr(&thePtr); // ask the converter
-
- if(theDescriptionPtr)
- *theDescriptionPtr = err ? NULL : thePtr;
-
- return err;
- }
-
- OSStatus psLowCanConvert(PSStream *stream, Collection hints, LowConverterInfo *dataInfo, Fixed *downloadability)
- {
- OSStatus err = noErr;
- PSSerialStream *readStream;
-
- if(stream->type == kPSRandomAccessStream)
- readStream = &(stream->u.file.serialStream);
- else{
- if(stream->type == kPSSerialStream)
- readStream = &stream->u.ps;
- else{
- err = errUnsupportedStream;
- }
- }
-
- *downloadability = CANTDOWNLOAD; // start by saying we can't download this file
-
- if(!err){
- err = converterCanConvert(readStream, stream, hints, dataInfo, downloadability);
- }
-
- if(err == errUnsupportedStream){
- err = noErr;
- *downloadability = CANTDOWNLOAD;
- }
-
- return err;
- }
-
- OSStatus psLowGetStreamInfo(PSStream *stream, Collection hints, DownloadDocumentInfo *downloadDocInfo)
- /* Here we return info about the document/stream.
- */
- {
- OSStatus err = noErr;
- kHintJobTypeVar jobType;
- kHintCopiesVar copies = 1;
- PSSerialStream *readStream;
- SInt32 numPages = 1; // Here we assume there is only one page. Converters that
- // have more than one page need to determine the correct number
- // and use it in place of this hard coded constant. If EPS was requested
- // then the number of pages should always be 1 and the first of multiple
- // pages is the only one that should be generated.
- OSType docType = '????';
-
- if(stream->type == kPSRandomAccessStream)
- readStream = &(stream->u.file.serialStream);
- else{
- if(stream->type == kPSSerialStream)
- readStream = &stream->u.ps;
- else{
- err = errUnsupportedStream;
- }
- }
-
- // determine what type of job was requested
- if(!err){
- err = getHint(hints, kHintJobTypeTag, kHintJobTypeId, sizeof(jobType), &jobType);
- if(err == collectionItemNotFoundErr){
- err = noErr;
- jobType = psJobPostScript;
- }
- }
-
- if(!err && jobType != psJobEPSNoPreview){
- // if we're not generating EPS, then the number of copies might be something other than 1.
- err = getHint(hints, kHintCopiesTag, kHintCopiesId, sizeof(copies), &copies);
- if(err == collectionItemNotFoundErr){
- copies = kHintCopiesDef;
- err = noErr;
- }
- }
-
- if(!err){
- // if we are not generating EPS we use psRequiresManualFeed in FeatureUtilsLib to look
- // at the hints collection and tell us whether the job requires manual feed. EPS jobs are
- // never manual feed.
- if(jobType != psJobEPSNoPreview){
- err = psRequiresManualFeed(hints, &(downloadDocInfo->isManualFeed));
- }else{
- downloadDocInfo->isManualFeed = false;
- }
- }
-
- // ask the shell client code what its document type is.
- if(!err){
- err = converterGetConverterDocType(readStream, stream, hints, &docType);
- }
-
- // fill in the data we collected
- downloadDocInfo->type = docType;
- downloadDocInfo->pages = numPages;
- downloadDocInfo->copies = copies;
-
- return err;
- }
-
- OSStatus psLowPeekConvert(PSStream *stream, Collection hints)
- {
- OSStatus err = noErr;
- PSSerialStream *readStream;
-
- if(stream->type == kPSRandomAccessStream)
- readStream = &(stream->u.file.serialStream);
- else{
- if(stream->type == kPSSerialStream)
- readStream = &stream->u.ps;
- else{
- err = errUnsupportedStream;
- }
- }
-
- if(!err)
- err = converterPeekConvert(readStream, stream, hints);
-
- return err;
- }
-
- #define BACKCHANNEL_BUFFER_SIZE 0x200
- OSStatus psLowDoConvert(PSStream *streamIn, PSStream *streamOut, Collection hints)
- {
- unsigned char *inDataBuffer = NULL;
- OSStatus err = noErr;
- PSSerialStream *readStream;
- Boolean doBinary = false, canDoGrayOnHost = false;
- UInt32 languageLevel = 1;
- Str255 converterName = "\p";
-
- if(streamIn->type == kPSRandomAccessStream)
- readStream = &(streamIn->u.file.serialStream);
- else{
- if(streamIn->type == kPSSerialStream)
- readStream = &streamIn->u.ps;
- else{
- err = errCantHandleThisDownloadData;
- }
- }
-
- if(!err){
- err = converterGetConverterName(converterName); // get the name from the converter
- }
-
- if(!err){
- PSPagesType thePages;
- thePages.pages = 1; // Again we always have 1 page for this shell code.
- // This should be adjusted if you have more than one page.
- thePages.order = kPSOrderAscending;
- if(!err)
- err = AddCollectionItem(hints, kHintPagesTypeTag, kHintPagesTypeId, sizeof(thePages), &thePages);
- }
-
- // allocate our back channel buffer so that we can read from the backchannel if there is one
- if(!err){
- inDataBuffer = (unsigned char *)psNewPtr(BACKCHANNEL_BUFFER_SIZE);
- if(!inDataBuffer){
- err = MemError();
- if(!err)err = memFullErr;
- }
- }
-
- if(!err && streamIn && streamOut){
- // developers should adjust this if there is more than one page
- SInt32 numPages = 1; // we always have one page in this shell code
-
- SInt32 pageNumber = 1; // when we emit a page it is always the 1st page
- Boolean isNotEPS = true;
- StreamInfoData comm = NULL;
-
- // process the results of our query. We initialize our data to our defaults so that we
- // operate as expected if for some reason our query hints aren't available
- kHintLanguageLevelVar langlevel = kHintLanguageLevelDef;
- kHintDataFormatVar eightBit = false;
- kHintTransparentChannelVar transparent = false;
- kHintColorDeviceVar isColorDevice = kHintColorDeviceDef;
- kHintColorSepVar doingColorSeps = kHintColorSepDef;
-
- err = getHint(hints, kHintLanguageLevelTag, kHintLanguageLevelId, sizeof(langlevel), &langlevel);
- if(err == collectionItemNotFoundErr)err = noErr;
- if(!err)err = getHint(hints, kHintEighthBitTag, kHintDataFormatId, sizeof(eightBit), &eightBit);
- if(err == collectionItemNotFoundErr)err = noErr;
- if(!err)err = getHint(hints, kHintTransparentChannelTag, kHintTransparentChannelId, sizeof(transparent), &transparent);
- if(err == collectionItemNotFoundErr)err = noErr;
-
- if(!err)err = getHint(hints, kHintColorDeviceTag, kHintColorDeviceId, sizeof(isColorDevice), &isColorDevice);
- if(err == collectionItemNotFoundErr)err = noErr;
-
- if(!err)err = getHint(hints, kHintColorSepTag, kHintColorSepId, sizeof(doingColorSeps), &doingColorSeps);
- if(err == collectionItemNotFoundErr)err = noErr;
-
- if(!err){
- kHintEPSBBoxVar bbox;
- void *clientData = NULL;
- doBinary = eightBit && transparent; // we can only do binary if we are transparent and 8 bit
-
- /* computeLanguageLevel computes the minimum PostScript language level we must support. Negative values
- of langlevel returned from the query typically correspond to save to disk options where we are
- to generate output compatible with a specific language level but it may be sent to a device which
- has a language level which is higher. This gives the PostScript producing code a chance to generate
- conditional code that takes advantage of advanced features while emulating those features in the
- target miminum language level. Here we simply use the minimum language level
- */
- languageLevel = computeLanguageLevel(langlevel);
-
- // can only do gray downsampling on host if the destination is definitely a BW printer
- // and it definitely isn't doing color seps
- canDoGrayOnHost = (doingColorSeps == kTriFalse) && (isColorDevice == kTriFalse);
-
- // determine whether we are doing EPS or PS
- if(!err){
- kHintJobTypeVar jobType = psJobEPSNoPreview;
- err = getHint(hints, kHintJobTypeTag, kHintJobTypeId, sizeof(jobType), &jobType);
- if(err == collectionItemNotFoundErr){
- err = noErr;
- jobType = psJobPostScript;
- }
-
- isNotEPS = jobType == psJobPostScript;
- }
-
- // get a StreamInfoData structure
- if(!err){
- err = psSetupStreamInfoData(&comm, streamOut, hints);
- if(!err){
- OSStatus tempErr = noErr;
-
- /* let the client initialize its data. The client should collect whatever information it needs
- to tell us the bounding box and generate its prolog, etc.
-
- Note that we are passing a POINTER to the language level which we expect to be filled in by
- the converterInitDoConvertClientData routine to be the lowest languagelevel (greater than 0!)
- that it is supporting.
- */
- err = converterInitDoConvertClientData(&clientData,
- readStream, streamIn, streamOut, hints,
- inDataBuffer, BACKCHANNEL_BUFFER_SIZE,
- &languageLevel, doBinary, canDoGrayOnHost, isNotEPS);
-
- if(!err){
-
- // let the client tell us what the bounding box is for this data. That bbox is used
- // to generate the autoscaling or %%BoundingBox comment, whichever is appropriate.
- if(!err){
- err = converterGetBBox(&bbox, clientData);
- }
-
- // we only add this hint if we are not doing EPS
- if(!err && isNotEPS){
- err = AddCollectionItem(hints, kHintEPSBBoxTag, kHintScalingBBoxId, sizeof(bbox), &bbox);
- }
-
- // these hints are potentially used by coverpage code to generate the application information
- // for the cover sheet
- if(!err){
- Str255 appNameString, tempStr;
- (void)pStrCopy(appNameString, converterName);
-
- err = AddCollectionItem(hints, kHintAppNameTag, kHintAppNameId, sizeof(appNameString), appNameString);
-
- if(!err)err = AddCollectionItem(hints, kHintClientNameTag, kHintClientNameId, sizeof(appNameString), appNameString);
-
- CopyCtoPstr(tempStr, kShortVersStr);
- (void)pStrCopy(appNameString, tempStr);
- #if qDebug
- CopyCtoPstr(tempStr, kqDebug);
- (void)pStrCat(appNameString, tempStr);
- #endif
- if(!err)err = AddCollectionItem(hints, kHintClientVersionTag, kHintClientVersionId, sizeof(appNameString), appNameString);
- }
-
- if(!err){
- // emit the PostScript code necessary to handle the image
-
- // write the %!PS-Adobe-3.0 comment appropriately.
- if(isNotEPS){
- Fixed dscVersion = DSC30Version;
- err = doOutputPosition(&psLowVersion, &dscVersion);
- }else{
- EPSFVersion epsfVersion;
- epsfVersion.dscVersion = DSC30Version;
- epsfVersion.epsfVersion = EPSF30Version;
-
- err = doOutputPosition(&psLowEPSFVersion, &epsfVersion);
- if(!err){
- DSCBBox dscBBox;
- dscBBox.llx = bbox.left;
- dscBBox.lly = bbox.bottom;
- dscBBox.urx = bbox.right;
- dscBBox.ury = bbox.top;
- err = psOutFormatPositionInfo(comm, &psLowBoundingBox, &dscBBox, (long)dscBBox.urx, (long)dscBBox.ury);
- }
- }
-
- // emit the data for the header including the creator
- if(!err)err = emitHintStr(comm, &psLowDocTitle, hints, kHintDocNameTag, kHintDocNameId, kHintDocNameDef);
- if(!err)err = emitHintStr(comm, &psLowDocFor, hints, kHintUserNameTag, kHintUserNameId, kHintUserNameDef);
- if(!err)err = emitHintStr(comm, &psLowDocRouting, hints, kHintRoutingTag, kHintRoutingId, NULL);
- if(!err)err = headerDate(comm);
- if(!err){
- Str255 creatorString, tempStr;
- (void)copyPStr(creatorString, converterName, sizeof(creatorString));
- (void)pStrCat(creatorString, "\p v");
- CopyCtoPstr(tempStr, kShortVersStr);
- (void)pStrCat(creatorString, tempStr);
- #if qDebug
- CopyCtoPstr(tempStr, kqDebug);
- (void)pStrCat(creatorString, tempStr);
- #endif
- err = psOutFormatPositionInfo(comm, &psLowDocCreator, creatorString, creatorString);
- }
-
- // this reflects the total number of pages in the document. For this sample code it
- // is hard coded to 1 page
- if(!err)err = psOutFormatPositionInfo(comm, &psLowPagesKnown, &numPages, numPages);
-
- // if we are generating PostScript language output for a language level greater than Level 1
- // then we generate the %%LanguageLevel comment
- if(!err && languageLevel > 1){
- err = psOutFormatPositionInfo(comm, &psLowLanguageLevel, &languageLevel, languageLevel);
- }
-
- // generate the %%DocumentData comment based on whether we are doing binary or not
- if(!err){
- if(doBinary){
- DSCDocumentData myData = kDSCBinary;
- err = psOutFormatPositionInfo(comm, &psLowDocDataBinary, &myData);
- }else{
- DSCDocumentData myData = kDSCClean7Bit;
- err = psOutFormatPositionInfo(comm, &psLowDocDataClean7Bit, &myData);
- }
- }
-
-
- if(!err)err = doOutputPosition(&psLowEndComments, NULL);
-
- if(!err)err = doOutputPosition(&psLowBeginProlog, NULL);
-
- // let the converter client emit its prolog
- if(!err)err = converterEmitProlog(comm, clientData);
-
- if(!err)err = doOutputPosition(&psLowEndProlog, NULL);
-
- if(!err)err = doOutputPosition(&psLowBeginSetup, NULL);
-
- if(!err)err = doOutputPosition(&psLowEndSetup, NULL);
-
-
- // the pageNumber used here reflects the number of the page that we are about to emit the
- // PostScript code for. This is the %%Page: comment. Since we are hard coded to do 1 page
- // we use the pageNumber here which was hardcoded to 1.
- if(!err){
- DSCPage dscPageStatus;
-
- dscPageStatus.ordinal = pageNumber;
- NumToString(pageNumber, dscPageStatus.label);
-
- err = psOutFormatPositionInfo(comm, &psLowPage, &dscPageStatus, pageNumber, pageNumber);
- }
-
- // we tell the output position call what page we are on so it can do the right thing for that
- // page
- if(!err){
- err = doOutputPosition(&psLowBeginPageSetup, &pageNumber);
- }
-
- /* for a one page graphic converter we can do our device adjust matrix before our other coordinate
- transformations for that page. For a multiple page converter we might have to be more careful
- if we create a matrix in the job setup section that we concat for each page.
- */
- if(!err){
- err = psOutPStr(comm, psBeforeDeviceMatrixAdjust);
- if(!err)err = psWriteSubsectionFeature(comm, hints, kSubDeviceAdjustMatrix, NULL, kBeforeSubsection, isNotEPS);
- if(!err)err = psOutPStr(comm, psAfterDeviceMatrixAdjust);
- }
-
- /* Here we generate our request for auto scaling. The feature utils lib code for autoscaling
- handles EPS correctly (i.e. generates no autoscaling code) and it also handles the hint
- kHintDoAutoScalingTag properly. Still, some converters may not want autoscaling and they
- should remove this code if that is the case.
- */
- if(!err)
- err = psWriteSubsectionFeature(comm, hints, kSubAutoScaling, NULL, kBeforeSubsection, isNotEPS);
-
-
- if(!err){
- err = doOutputPosition(&psLowEndPageSetup, &pageNumber);
- }
- }
-
- // ask the shell client to generate the data for this page
- if(!err){
- err = converterEmitPageData(comm, clientData);
- }
-
- // make sure the backchannel data is read properly.
- if(!err)
- err = ReadWriteBackChannel(streamIn, readStream->write, streamOut, streamOut->u.ps.read, inDataBuffer, BACKCHANNEL_BUFFER_SIZE);
-
- if(!err){
- // emit the PostScript code necessary to complete the image. If the shell client needs
- // to generate showpage then this line should be removed
- err = psOutFormatPosition(comm, &psLowShowPage);
-
- // emit the page trailer data
- if(!err)
- err = doOutputPosition(&psLowPageTrailer, &pageNumber);
-
- // emit the document trailer data
- if(!err)
- err = doOutputPosition(&psLowDocTrailer, NULL);
-
- // We're done so write the EOF.
- if(!err)
- err = doOutputPosition(&psLowEOF, NULL);
-
- // read any last data.
- if(!err)
- err = ReadWriteBackChannel(streamIn, readStream->write, streamOut, streamOut->u.ps.read, inDataBuffer, BACKCHANNEL_BUFFER_SIZE);
-
- }
- // give the client a chance to clean up its data
- if(clientData){
- tempErr = converterDisposeDoConvertClientData(clientData);
- clientData = NULL;
- if(!err)err = tempErr;
- }
- }
- tempErr = psDisposeStreamInfoData(&comm);
- if(!err)err = tempErr;
- }
- }
- }
- }
-
- psDisposePtr(inDataBuffer);
-
- return err;
- }
- #undef BACKCHANNEL_BUFFER_SIZE
-
- OSErr psLowGetConverterVersion(struct CFMVersion *version)
- {
- return converterGetVersion(version);
- }
-
- OSStatus psLowAddConverterQueries(Collection hints, Collection query)
- /* Here we decide what queries we need to add.
- */
- {
- OSStatus err = noErr;
- kHintLanguageLevelVar defaultLevel = kHintLanguageLevelDef;
- Unused(hints);
-
- err = AddCollectionItem(query, kHintLanguageLevelTag, kHintLanguageLevelId, sizeof(defaultLevel), &defaultLevel);
- if(!err){
- kHintDataFormatVar eightBit = false; // conservative defaults
- err = AddCollectionItem(query, kHintEighthBitTag, kHintEighthBitId, sizeof(eightBit), &eightBit);
- }
- if(!err){
- kHintTransparentChannelVar transparent = false; // conservative defaults
- err = AddCollectionItem(query, kHintTransparentChannelTag, kHintTransparentChannelId, sizeof(transparent), &transparent);
- }
- // add color device related queries
- if(!err){
- kHintColorDeviceVar isColorDevice = kHintColorDeviceDef; // unknown whether we are color device
- err = AddCollectionItem(query, kHintColorDeviceTag, kHintColorDeviceId, sizeof(isColorDevice), &isColorDevice);
- }
-
- if(!err){
- kHintColorSepVar doingColorSeps = kHintColorSepDef; // unknown whether we are doing color separations
- err = AddCollectionItem(query, kHintColorSepTag, kHintColorSepId, sizeof(doingColorSeps), &doingColorSeps);
-
- // if the hint is already there and locked, then that is fine.
- if(err == collectionItemLockedErr)err = noErr;
-
- }
-
- if(!err)
- err = converterAddAdditionalQueries(hints, query);
-
- return err;
- }
-
-
- #if PRAGMA_IMPORT_SUPPORTED
- #pragma export off
- #endif
-
- OSStatus ReadWriteBackChannel(PSStream *streamToClient, PSWriteProc write,
- PSStream *streamToPrinter, PSReadProc read,
- unsigned char *inDataBuffer, long bufferSize)
- {
- OSStatus err = noErr;
- if(read){
- err = read(streamToPrinter, inDataBuffer, &bufferSize);
- // write any data we read from backchannel back out to client
- if(!err && bufferSize && write)err = write(streamToClient, inDataBuffer, bufferSize);
- }
- return err;
- }
-
- static OSErr emitHintStr(StreamInfoData comm, const SubsectionStr *form, Collection hints, CollectionTag tag, long id,
- ConstStringPtr defaultStr)
- /* This function can be used to emit the PostScript specified by 'form' to 'comm.
- The 'form' format string is supposed to have a Pascal string parameter. This
- routine gets the Pascal string by looking in 'hints' for a hint specified by
- 'tag' and 'id'. If the hint does not exist then this routine will use the
- default Pascal string supplied by 'defaultStr'. If 'defaultStr' is NULL then
- nothing is emitted by this routine. This routine will only return an error
- if there is a problem emitting a string. A missing hint does not return an error.
- */
- {
- Str255 str;
- OSStatus err;
-
- err = GetCollectionStrOrDef(hints, tag, id, str, sizeof(str), (StringPtr)defaultStr);
- if(!err){
- err = psOutFormatPositionInfo(comm, form, str, str); //
- }else{
- err = noErr; // A missing hint isn't an error.
- }
-
- return err;
- }
-
- static OSStatus GetCollectionStrOrDef(Collection hints, CollectionTag tag, long id,
- StringPtr str, long strSize, StringPtr defaultStr)
- /* Look for the Pascal String hint specified by 'tag' and 'id' in 'hints'.
- Copy the string into the buffer pointed to by 'str'. The caller guarentees
- that 'str' points to at least 'strSize' bytes. If the hint can not be found
- and 'defaultStr' is NULL, then the err collectionItemNotFoundErr is returned.
- If the hint is not found and 'defaultStr' is not NULL, then the pascal
- string pointed to by 'defaultStr' is copied into 'str', truncated if necessary.
- */
- {
- OSStatus err;
- long itemSize = strSize;
-
- str[0] = 0; // Start with an empty string.
- err = GetCollectionItem(hints, tag, id, &itemSize, str);
-
- if(err == collectionItemNotFoundErr && defaultStr != NULL){ // Use the default if we have to.
- copyPStr(str, defaultStr, strSize);
- err = noErr;
- }
-
- return err;
- }
-
-
- static OSStatus headerDate(StreamInfoData comm)
- {
- unsigned long timeNow;
- Str63 str;
- StringPtr spaceSpot;
-
- GetDateTime(&timeNow);
- IUTimeString(timeNow, false, str);
- spaceSpot = str + *str + 1;
- IUDateString(timeNow, longDate, spaceSpot);
- *str = (Byte)(*str + *spaceSpot + 1);
- *spaceSpot = ' ';
-
- return psOutFormatPositionInfo(comm, &psLowDocCreationDate, str, str);
- }
-
- static OSStatus OutputPosition(StreamInfoData comm, Collection hints, const SubsectionStr *subsectionStr, void *info, Boolean isNotEPS)
- {
- OSStatus err = noErr;
-
- err = psWriteSubsectionFeature(comm, hints, subsectionStr->subsection, info, kBeforeSubsection, isNotEPS);
- if(!err){
- if(info)
- err = psOutFormatPositionInfo(comm, subsectionStr, info);
- else
- err = psOutFormatPosition(comm, subsectionStr);
- }
- if(!err)err = psWriteSubsectionFeature(comm, hints, subsectionStr->subsection, info, kAfterSubsection, isNotEPS);
-
- return err;
-
- }
-
- OSStatus writeLogMsg(PSStream *streamOut, PSSubsection subsection, void *info, SInt32 stringsID, SInt32 msgID, Boolean isError)
- /* This routine is to be called by a converter to log any error or warning messages which are appropriate
- during the data conversion.
-
- streamOut is the write stream passed to the low level converter's converterInitDoConvertClientData proc
-
- subsection is the PSSubsection for which the error pertains. Use kSubAnon if there is no appropriate subsection
-
- info is a pointer to a structure appropriate for the subsection being reported or is NULL.
-
- stringsID is the ID of a STR# resource containing the message string list for the converter. The
- converter library will be opened by writeLogMsg to obtain the STR# resource and use it so
- a client need not open the library before calling writeLogMsg.
-
- msgID is the list number of the target message within the STR# resource.
-
- isError is LOGERROR if the caller wants the message to be reported as an error as opposed to a warning. The
- constant LOGWARNING is used to report the message as a warning.
- */
- {
- OSStatus err = noErr;
- PSSerialStream *stream;
-
- if(streamOut->type == kPSRandomAccessStream)
- stream = &(streamOut->u.file.serialStream);
- else{
- if(streamOut->type == kPSSerialStream)
- stream = &streamOut->u.ps;
- else{
- err = errCantHandleThisDownloadData;
- }
- }
-
- if(!err){
- DSCLogData logData;
- short libRes, holdResFile;
-
- /* The reason we open the library resource fork here rather than in the library init proc
- is that we are using shared global data so our init proc only is called when the library
- is initially opened. If we were to open the library and save the
- fref, that would work for the caller who made the first call to the library, but any
- subsequent caller into the already opened library wouldn't have the resource
- fork in its resource chain and our resource calls would fail. Instead the shell code
- saves the library FSSpec and we need to open (and close) the library when we need it.
- No big deal but if we don't respect this, we'll have problems when there are multiple
- downloads going on simultaneously.
- */
- holdResFile = CurResFile();
- err = openLowLibraryResFile(&libRes);
-
- if(!err){
- GetIndString(logData.logMessage, stringsID, msgID);
- CloseResFile(libRes);
- }
- UseResFile(holdResFile);
-
- // build the data to put in the info field of the PSPosition data for the call
- // the subsection on the log data is whatever the caller asked for
- logData.logSubsection = subsection;
-
- // the info on the log data is whatever the caller asked for
- logData.info = info;
-
- // the subsection on our write depends whether the caller wanted it to be a warning or error message
- stream->pos.subsection = isError ? kSubLogErrorData : kSubLogWarningData;
-
- // the info field is the logdata that we created based on the callers request
- stream->pos.info = &logData;
- stream->pos.id++;
-
- // we are writing no data but are doing the write just to pass along the data in the PSPosition on streamOut
- err = stream->write(streamOut, NULL, 0);
-
- // reset the PSPosition data after our write call
- stream->pos.subsection = kSubAnon;
- stream->pos.info = NULL;
- stream->pos.id++;
-
- }
-
- return err;
- }
-
- OSErr openLowLibraryResFile(short *fRef)
- /* Open the low level converter's resource fork using the permission flags
- from 'permission'. Place the fRef of the open file into *'fRef'.
- If the file can not be opened, then an error is returned and
- *'fRef' is set to -1. The caller should use CloseResFile() to
- close *'fRef'. rb840
- */
- {
- OSErr err;
-
- *fRef = FSpOpenResFile(&gLowConverterFileSpec, fsRdPerm);
- err = ResError();
- if(err){
- *fRef = -1;
- }
-
- return err;
- }
-
- static UInt32 computeLanguageLevel(SInt32 hintLanguageLevel)
- /*
- This routine returns the minimum language level which is required by the hintLanguageLevel passed
- in. hintLanguageLevel can correspond to requests to support multiple language levels, such as
- "Level 1 Compatible" or "Level 2 Compatible" without specifying exactly what language level the target
- device actually contains.
- */
- {
- UInt32 languageLevel = hintLanguageLevel;
-
- if(hintLanguageLevel < 1){
- // negative language level values mean different things
- if(hintLanguageLevel <= Level1and2){
- // this is level1and2, level2and3, level3and4, ...
- languageLevel = -hintLanguageLevel - 1;
- }else{
- // this is language level other or unknown
- languageLevel = 1;
- }
- }
- return languageLevel;
- }
-